package huffman;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author 谭继好
 * @Description
 * @Date 2024-05-19 16:06
 */
public class ZipWithThreadPool {

    // 调参：
    private static final int MAX_SIZE = 1000000;
    private ThreadPoolExecutor zip = new ThreadPoolExecutor(
            20, 50, 2,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy());
    private ThreadPoolExecutor unZip = new ThreadPoolExecutor(
            20, 50, 2,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy());

    public void zip(String path) {

        // 按容量划分线程，结果存储到哈希表中，拼串后写入文件
        Long filelength = new File(path).length();
        int len = (int) (filelength / MAX_SIZE);
        Map<Integer, Future<StringBuilder>> map = new HashMap<>(len + 1);

        // 多线程读取文件建树
        for (int i = 0; i < len + 1; i++) {
            Utils.ZipThread thread = new Utils.ZipThread(path, (int) Math.min(MAX_SIZE, filelength));
            Future<StringBuilder> res = zip.submit(thread);
            filelength -= MAX_SIZE;
            map.put(i, res);
        }

        // 写出到文件
        Utils.writeToFile(path.replace(".txt", "-zip.txt"), map);
        zip.shutdown();
    }

    public void unzip(String path) {

        // 读取文件全部内容并以"竺"分隔，分成编码子序列
        String content = Utils.readAllFile(path);
        String[] split = content.split("竺");

        // 注意：最后的"竺"并不会多出来一个空串，所以长度恰好是个数
        // 多线程解码并放到哈希表中，便于最后的结果输出
        Map<Integer, Future<StringBuilder>> map = new HashMap<>(split.length);
        for (int i = 0; i < split.length; i++) {
            Utils.UnZipThread thread = new Utils.UnZipThread(split[i]);
            Future<StringBuilder> res = unZip.submit(thread);
            map.put(i, res);
        }

        // 统一写出到文件，保证输出顺序，没办法。多路同时写也会有线程安全问题
        Utils.writeToFile(path.replace("-zip.txt", "-recover.txt"), map);
        unZip.shutdown();
    }

}
